#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/leds.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
#include <linux/err.h>

struct led_gpio_data {
	struct led_classdev led_dev;
	unsigned gpio;
	u8 active_low;
};


struct led_gpio_platform_data
{
	unsigned gpio;
	u8 active_low;
	u8 default_state;
	char *name;
	char *default_trigger;
};

static void gpio_led_set(struct led_classdev *led_cdev,
	enum led_brightness value)
{
	struct led_gpio_data *led_dat = container_of(led_cdev, struct led_gpio_data, led_dev);
	int gpio_value = 0;

	if (value == LED_OFF)
		gpio_value = 0;
	else
		gpio_value = 1;
	
	if(led_dat->active_low)
		gpio_value = !gpio_value;

	gpio_set_value(led_dat->gpio, gpio_value);
}




static int gpio_led_probe(struct platform_device *pdev)
{
	struct led_gpio_platform_data *platform_data = pdev->dev.platform_data;
	int ret = 0;
	struct led_gpio_data *led_data = NULL;
	int default_set_value = 0;
	printk("%s:%d\n", __FUNCTION__, __LINE__);

	led_data = devm_kzalloc(&pdev->dev, sizeof(struct led_gpio_data), GFP_KERNEL);
	if(led_data == NULL)
		return -ENOMEM;

	led_data->active_low = platform_data->active_low;
	led_data->gpio = platform_data->gpio;
	led_data->led_dev.brightness_set = gpio_led_set;
	led_data->led_dev.name = platform_data->name;
	led_data->led_dev.default_trigger = platform_data->default_trigger;

	default_set_value = platform_data->default_state > 0?1:0;
	if(led_data->active_low)
		default_set_value = !default_set_value;

	if(default_set_value)
		default_set_value = GPIOF_OUT_INIT_HIGH;
	else
		default_set_value = GPIOF_OUT_INIT_LOW;

	ret = devm_gpio_request_one(&pdev->dev, led_data->gpio, default_set_value, led_data->led_dev.name);
	if(ret)
	{
		return ret;
	}

	ret = led_classdev_register(&pdev->dev, &led_data->led_dev);
	if (ret < 0)
		return ret;

	platform_set_drvdata(pdev, led_data);

	return 0;
}

static int gpio_led_remove(struct platform_device *pdev)
{
	struct led_gpio_data *led_data = platform_get_drvdata(pdev);
	
	printk("%s:%d\n", __FUNCTION__, __LINE__);

	led_classdev_unregister(&led_data->led_dev);
	printk("%s:%d\n", __FUNCTION__, __LINE__);

	platform_set_drvdata(pdev, NULL);

	return 0;
}

static struct platform_driver gpio_led_driver = {
	.probe		= gpio_led_probe,
	.remove		= gpio_led_remove,
	.driver		= {
		.name	= "led_gpio",
		.owner	= THIS_MODULE,
	},
};

module_platform_driver(gpio_led_driver);

MODULE_AUTHOR("jerry_chg");
MODULE_DESCRIPTION("GPIO LED device driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:led_gpio");
